/*
 * Sylvain BERTRAND <digital.ragnarok@gmail.com>
 * code protected by GNU GPL v3
 */
#include <X11/Xlib.h>
#include <X11/keysym.h>
#include <X11/extensions/sync.h>
#include <glib.h>
#include <libxml/xinclude.h>
#include "../toolkit/xml.h"
#include "../toolkit/prop.h"
#include "../toolkit/display.h"
#include "../toolkit/keyboard.h"
#include "../toolkit/xqueue.h"
#include "misc.h"
#include "moveresize.h"
#include "window.h"
#include "geom.h"
#include "stacking.h"
#include "client.h"
#include "config.h"
#include "place.h"
#include "frame.h"
#include "wm.h"
#include "screen.h"
#include "grab.h"
#include "resist.h"
#include "event.h"
#include "focus.h"
#define KEY_DIST 8
#define SYNC_TIMEOUTS 4
gboolean moveresize_in_progress = FALSE;
struct wm_client *moveresize_client = NULL;
XSyncAlarm moveresize_alarm = None;
static gboolean moving = FALSE;
static gint start_x, start_y, start_cx, start_cy, start_cw, start_ch;
static gboolean was_max_horz, was_max_vert;
static struct wm_rect pre_max_area;
static gint cur_x, cur_y, cur_w, cur_h;
static guint button;
static enum wm_moveresize_type cur_type;
static enum wm_direction edge_warp_dir = -1;
static gboolean edge_warp_odd = FALSE;
static guint edge_warp_timer = 0;
static enum wm_direction key_resize_edge = -1;
static guint waiting_for_sync;
static guint sync_timer = 0;
static void client_dest(struct wm_client *client, gpointer data)
{
	(void)data;
	if(moveresize_client == client)
		moveresize_end(TRUE);
}

void moveresize_startup(void)
{
	client_add_destroy_notify(client_dest, NULL);
}

void moveresize_shutdown(void)
{
	if(moveresize_in_progress)
		moveresize_end(FALSE);
	client_remove_destroy_notify(client_dest);
}

void moveresize_start(struct wm_client *c, gint x, gint y, guint b, enum wm_moveresize_type type)
{
	enum wm_cursor cur;
	gboolean mv = (type == WM_MOVE || type == WM_MOVE_KEYBOARD);
	gint up = 1;
	gint left = 1;
	if(moveresize_in_progress || !c->frame->visible)
		return;
	if(type == WM_SIZE_TOPLEFT) {
		cur = WM_CURSOR_NORTHWEST;
		up = left = -1;
	} else if(type == WM_SIZE_TOP) {
		cur = WM_CURSOR_NORTH;
		up = -1;
	} else if(type == WM_SIZE_TOPRIGHT) {
		cur = WM_CURSOR_NORTHEAST;
		up = -1;
	} else if(type == WM_SIZE_RIGHT)
		cur = WM_CURSOR_EAST;
	else if(type == WM_SIZE_BOTTOMRIGHT)
		cur = WM_CURSOR_SOUTHEAST;
	else if(type == WM_SIZE_BOTTOM)
		cur = WM_CURSOR_SOUTH;
	else if(type == WM_SIZE_BOTTOMLEFT) {
		cur = WM_CURSOR_SOUTHWEST;
		left = -1;
	} else if(type == WM_SIZE_LEFT) {
		cur = WM_CURSOR_WEST;
		left = -1;
	} else if(type == WM_SIZE_KEYBOARD)
		cur = WM_CURSOR_SOUTHEAST;
	else if(type == WM_MOVE || type == WM_MOVE_KEYBOARD)
		cur = WM_CURSOR_MOVE;
	else
		g_assert_not_reached();
	if(!grab_pointer(FALSE, TRUE, cur))
		return;
	if(!grab_keyboard()) {
		ungrab_pointer();
		return;
	}
	moving = mv;
	moveresize_client = c;
	start_cx = c->area.x;
	start_cy = c->area.y;
	start_cw = c->area.width;
	start_ch = c->area.height;
	start_x = x - (mv ? 0 : left * c->size_inc.width / 2);
	start_y = y - (mv ? 0 : up * c->size_inc.height / 2);
	cur_type = type;
	button = b;
	key_resize_edge = -1;
	was_max_horz = was_max_vert = FALSE;
	cur_x = start_cx;
	cur_y = start_cy;
	cur_w = start_cw;
	cur_h = start_ch;
	moveresize_in_progress = TRUE;
	waiting_for_sync = 0;
	if(config_resize_redraw && !moving && t_display_extension_sync && moveresize_client->sync_request
		&& moveresize_client->sync_counter) {
		XSyncValue val;
		XSyncAlarmAttributes aa;
		XSyncIntToValue(&val, 0);
		XSyncSetCounter(t_display, moveresize_client->sync_counter, val);
		moveresize_client->sync_counter_value = 0;
		XSyncIntToValue(&val, 1);
		aa.trigger.counter = moveresize_client->sync_counter;
		aa.trigger.wait_value = val;
		aa.trigger.value_type = XSyncAbsolute;
		aa.trigger.test_type = XSyncPositiveTransition;
		aa.events = True;
		XSyncIntToValue(&aa.delta, 1);
		moveresize_alarm =
			XSyncCreateAlarm(t_display,
			XSyncCACounter | XSyncCAValue | XSyncCAValueType | XSyncCATestType | XSyncCADelta | XSyncCAEvents, &aa);
	}
}

static void cancel_edge_warp(void)
{
	if(edge_warp_timer)
		g_source_remove(edge_warp_timer);
	edge_warp_timer = 0;
}

void moveresize_end(gboolean cancel)
{
	ungrab_keyboard();
	ungrab_pointer();
	if(!moving) {
		if(moveresize_alarm != None) {
			XSyncDestroyAlarm(t_display, moveresize_alarm);
			moveresize_alarm = None;
		}
		if(sync_timer)
			g_source_remove(sync_timer);
		sync_timer = 0;
	}
	client_configure(moveresize_client, (cancel ? start_cx : cur_x), (cancel ? start_cy : cur_y), (cancel ? start_cw : cur_w),
		(cancel ? start_ch : cur_h), TRUE, TRUE, FALSE);
	if(cancel && (was_max_horz || was_max_vert)) {
		const gboolean h = moveresize_client->max_horz;
		const gboolean v = moveresize_client->max_vert;
		client_maximize(moveresize_client, TRUE, was_max_horz && was_max_vert ? 0 : (was_max_horz ? 1 : 2));
		if(was_max_horz && !h) {
			moveresize_client->pre_max_area.x = pre_max_area.x;
			moveresize_client->pre_max_area.width = pre_max_area.width;
		}
		if(was_max_vert && !v) {
			moveresize_client->pre_max_area.y = pre_max_area.y;
			moveresize_client->pre_max_area.height = pre_max_area.height;
		}
	}
	cancel_edge_warp();
	moveresize_in_progress = FALSE;
	moveresize_client = NULL;
}

static void do_move(gboolean keyboard, gint keydist)
{
	gint resist;
	if(keyboard)
		resist = keydist - 1;
	else
		resist = config_resist_win;
	resist_move_windows(moveresize_client, resist, &cur_x, &cur_y);
	if(!keyboard)
		resist = config_resist_edge;
	resist_move_monitors(moveresize_client, resist, &cur_x, &cur_y);
	client_configure(moveresize_client, cur_x, cur_y, cur_w, cur_h, TRUE, FALSE, FALSE);
}

static void do_resize(void);
static gboolean sync_timeout_func(gpointer data)
{
	(void)data;
	++waiting_for_sync;
	do_resize();
	if(waiting_for_sync > SYNC_TIMEOUTS) {
		sync_timer = 0;
		return FALSE;
	} else
		return TRUE;
}

static void do_resize(void)
{
	gint x, y, w, h, lw, lh;
	x = cur_x;
	y = cur_y;
	w = cur_w;
	h = cur_h;
	client_try_configure(moveresize_client, &x, &y, &w, &h, &lw, &lh, TRUE);
	if(!(w == moveresize_client->area.width && h == moveresize_client->area.height) && (waiting_for_sync == 0
			|| waiting_for_sync > SYNC_TIMEOUTS)) {
		if(config_resize_redraw && t_display_extension_sync && waiting_for_sync == 0 && moveresize_client->sync_request
			&& moveresize_client->sync_counter) {
			XEvent ce;
			XSyncValue val;
			++moveresize_client->sync_counter_value;
			XSyncIntToValue(&val, moveresize_client->sync_counter_value);
			ce.xclient.type = ClientMessage;
			ce.xclient.message_type = T_PROP_ATOM(WM_PROTOCOLS);
			ce.xclient.display = t_display;
			ce.xclient.window = moveresize_client->window;
			ce.xclient.format = 32;
			ce.xclient.data.l[0] = T_PROP_ATOM(NET_WM_SYNC_REQUEST);
			ce.xclient.data.l[1] = event_time();
			ce.xclient.data.l[2] = XSyncValueLow32(val);
			ce.xclient.data.l[3] = XSyncValueHigh32(val);
			ce.xclient.data.l[4] = 0l;
			XSendEvent(t_display, moveresize_client->window, FALSE, NoEventMask, &ce);
			waiting_for_sync = 1;
			if(sync_timer)
				g_source_remove(sync_timer);
			sync_timer = g_timeout_add(2000, sync_timeout_func, NULL);
		}
		client_configure(moveresize_client, cur_x, cur_y, cur_w, cur_h, TRUE, FALSE, TRUE);
	}
}

static void calc_resize(gboolean keyboard, gint keydist, gint *dw, gint *dh, enum wm_direction dir)
{
	gint resist, x = 0, y = 0, lw, lh, ow, oh, nw, nh;
	gint trydw, trydh;
	ow = cur_w;
	oh = cur_h;
	nw = ow + *dw;
	nh = oh + *dh;
	if(!keyboard && (moveresize_client->max_ratio || moveresize_client->min_ratio)) {
		switch (dir) {
		case WM_DIRECTION_NORTH:
		case WM_DIRECTION_SOUTH:
			if(moveresize_client->min_ratio) {
				if(nh * moveresize_client->min_ratio > nw)
					nw = (gint)(nh * moveresize_client->min_ratio);
			}
			if(moveresize_client->max_ratio) {
				if(nh * moveresize_client->max_ratio < nw)
					nw = (gint)(nh * moveresize_client->max_ratio);
			}
			break;
		default:
			if(moveresize_client->min_ratio) {
				if(nh * moveresize_client->min_ratio > nw)
					nh = (gint)(nw / moveresize_client->min_ratio);
			}
			if(moveresize_client->max_ratio) {
				if(nh * moveresize_client->max_ratio < nw)
					nh = (gint)(nw / moveresize_client->max_ratio);
			}
			break;
		}
		client_try_configure(moveresize_client, &x, &y, &nw, &nh, &lw, &lh, TRUE);
		trydw = nw - ow;
		trydh = nh - oh;
	}
	nw += moveresize_client->frame->size.left + moveresize_client->frame->size.right;
	nh += moveresize_client->frame->size.top + moveresize_client->frame->size.bottom;
	if(keyboard)
		resist = keydist - 1;
	else
		resist = config_resist_win;
	resist_size_windows(moveresize_client, resist, &nw, &nh, dir);
	if(!keyboard)
		resist = config_resist_edge;
	resist_size_monitors(moveresize_client, resist, &nw, &nh, dir);
	nw -= moveresize_client->frame->size.left + moveresize_client->frame->size.right;
	nh -= moveresize_client->frame->size.top + moveresize_client->frame->size.bottom;
	*dw = nw - ow;
	*dh = nh - oh;
	if(!keyboard && (moveresize_client->max_ratio || moveresize_client->min_ratio)) {
		if(*dh != trydh) {
			if(moveresize_client->min_ratio) {
				if(nh * moveresize_client->min_ratio > nw)
					nw = (gint)(nh * moveresize_client->min_ratio);
			}
			if(moveresize_client->max_ratio) {
				if(nh * moveresize_client->max_ratio < nw)
					nw = (gint)(nh * moveresize_client->max_ratio);
			}
		}
		if(*dw != trydw) {
			if(moveresize_client->min_ratio) {
				if(nh * moveresize_client->min_ratio > nw)
					nh = (gint)(nw / moveresize_client->min_ratio);
			}
			if(moveresize_client->max_ratio) {
				if(nh * moveresize_client->max_ratio < nw)
					nh = (gint)(nw / moveresize_client->max_ratio);
			}
		}
	}
	client_try_configure(moveresize_client, &x, &y, &nw, &nh, &lw, &lh, TRUE);
	*dw = nw - ow;
	*dh = nh - oh;
}

static void edge_warp_move_ptr(void)
{
	gint x, y;
	const struct wm_rect *a;
	screen_pointer_pos(&x, &y);
	a = screen_physical_area_all_monitors();
	switch (edge_warp_dir) {
	case WM_DIRECTION_NORTH:
		y = a->height - 1;
		break;
	case WM_DIRECTION_EAST:
		x = a->x;
		break;
	case WM_DIRECTION_SOUTH:
		y = a->y;
		break;
	case WM_DIRECTION_WEST:
		x = a->width - 1;
		break;
	default:
		g_assert_not_reached();
	}
	XWarpPointer(t_display, 0, DefaultRootWindow(t_display), 0, 0, 0, 0, x, y);
}

static gboolean edge_warp_delay_func(gpointer data)
{
	guint d;
	(void)data;
	if(edge_warp_odd) {
		d = screen_find_desktop(screen_desktop, edge_warp_dir, TRUE, FALSE);
		if(d != screen_desktop) {
			if(config_mouse_screenedgewarp)
				edge_warp_move_ptr();
			screen_set_desktop(d, TRUE);
		}
	}
	edge_warp_odd = !edge_warp_odd;
	return TRUE;
}

static void do_edge_warp(gint x, gint y)
{
	guint i;
	enum wm_direction dir;
	if(!config_mouse_screenedgetime)
		return;
	dir = -1;
	for(i = 0; i < screen_num_monitors; ++i) {
		const struct wm_rect *a = screen_physical_area_monitor(i);
		if(x == RECT_LEFT(*a))
			dir = WM_DIRECTION_WEST;
		if(x == RECT_RIGHT(*a))
			dir = WM_DIRECTION_EAST;
		if(y == RECT_TOP(*a))
			dir = WM_DIRECTION_NORTH;
		if(y == RECT_BOTTOM(*a))
			dir = WM_DIRECTION_SOUTH;
		if((x + 1 == RECT_LEFT(*a) || x - 1 == RECT_RIGHT(*a)) && (dir == WM_DIRECTION_WEST
				|| dir == WM_DIRECTION_EAST)) {
			dir = -1;
		}
		if((y + 1 == RECT_TOP(*a) || y - 1 == RECT_BOTTOM(*a)) && (dir == WM_DIRECTION_NORTH
				|| dir == WM_DIRECTION_SOUTH)) {
			dir = -1;
		}
	}
	if(dir != edge_warp_dir) {
		cancel_edge_warp();
		if(dir != (enum wm_direction)-1) {
			edge_warp_odd = TRUE;
			edge_warp_timer = g_timeout_add(config_mouse_screenedgetime, edge_warp_delay_func, NULL);
		}
		edge_warp_dir = dir;
	}
}

static void move_with_keys(KeySym sym, guint state)
{
	gint dx = 0, dy = 0, ox = cur_x, oy = cur_y;
	gint opx, px, opy, py;
	gint dist = 0;
	if(state & t_keyboard_modkey_to_modmask(T_KEYBOARD_MODKEY_SHIFT)) {
		gint x, y;
		enum wm_direction dir;
		if(sym == XK_Right)
			dir = WM_DIRECTION_EAST;
		else if(sym == XK_Left)
			dir = WM_DIRECTION_WEST;
		else if(sym == XK_Down)
			dir = WM_DIRECTION_SOUTH;
		else
			dir = WM_DIRECTION_NORTH;
		client_find_move_directional(moveresize_client, dir, &x, &y);
		dx = x - moveresize_client->area.x;
		dy = y - moveresize_client->area.y;
	} else {
		if(state & t_keyboard_modkey_to_modmask(T_KEYBOARD_MODKEY_CONTROL)) {
			dist = 1;
		} else
			dist = KEY_DIST;
		if(sym == XK_Right)
			dx = dist;
		else if(sym == XK_Left)
			dx = -dist;
		else if(sym == XK_Down)
			dy = dist;
		else
			dy = -dist;
	}
	screen_pointer_pos(&opx, &opy);
	XWarpPointer(t_display, None, None, 0, 0, 0, 0, dx, dy);
	XSync(t_display, FALSE); {
		XEvent ce;
		while(xqueue_remove_local(&ce, xqueue_match_type, GINT_TO_POINTER(MotionNotify)));
	}
	screen_pointer_pos(&px, &py);
	cur_x += dx;
	cur_y += dy;
	do_move(TRUE, dist);
	start_x += (px - opx) - (cur_x - ox);
	start_y += (py - opy) - (cur_y - oy);
}

static void resize_with_keys(KeySym sym, guint state)
{
	gint dw = 0, dh = 0, pdx = 0, pdy = 0, opx, opy, px, py;
	gint resist = 0;
	enum wm_direction dir;
	if(sym == XK_Right) {
		dir = WM_DIRECTION_EAST;
		if(key_resize_edge != WM_DIRECTION_WEST && key_resize_edge != WM_DIRECTION_EAST) {
			key_resize_edge = WM_DIRECTION_EAST;
			return;
		}
	} else if(sym == XK_Left) {
		dir = WM_DIRECTION_WEST;
		if(key_resize_edge != WM_DIRECTION_WEST && key_resize_edge != WM_DIRECTION_EAST) {
			key_resize_edge = WM_DIRECTION_WEST;
			return;
		}
	} else if(sym == XK_Up) {
		dir = WM_DIRECTION_NORTH;
		if(key_resize_edge != WM_DIRECTION_NORTH && key_resize_edge != WM_DIRECTION_SOUTH) {
			key_resize_edge = WM_DIRECTION_NORTH;
			return;
		}
	} else {
		dir = WM_DIRECTION_SOUTH;
		if(key_resize_edge != WM_DIRECTION_NORTH && key_resize_edge != WM_DIRECTION_SOUTH) {
			key_resize_edge = WM_DIRECTION_SOUTH;
			return;
		}
	}
	if(state & t_keyboard_modkey_to_modmask(T_KEYBOARD_MODKEY_SHIFT)) {
		gint x, y, w, h;
		if(sym == XK_Right)
			dir = WM_DIRECTION_EAST;
		else if(sym == XK_Left)
			dir = WM_DIRECTION_WEST;
		else if(sym == XK_Down)
			dir = WM_DIRECTION_SOUTH;
		else
			dir = WM_DIRECTION_NORTH;
		client_find_resize_directional(moveresize_client, key_resize_edge, key_resize_edge == dir, &x, &y, &w, &h);
		dw = w - moveresize_client->area.width;
		dh = h - moveresize_client->area.height;
	} else {
		gint distw, disth;
		if(moveresize_client->size_inc.width > 1) {
			distw = moveresize_client->size_inc.width;
			resist = 1;
		} else if(state & t_keyboard_modkey_to_modmask(T_KEYBOARD_MODKEY_CONTROL)) {
			distw = 1;
			resist = 1;
		} else {
			distw = KEY_DIST;
			resist = KEY_DIST;
		}
		if(moveresize_client->size_inc.height > 1) {
			disth = moveresize_client->size_inc.height;
			resist = 1;
		} else if(state & t_keyboard_modkey_to_modmask(T_KEYBOARD_MODKEY_CONTROL)) {
			disth = 1;
			resist = 1;
		} else {
			disth = KEY_DIST;
			resist = KEY_DIST;
		}
		if(key_resize_edge == WM_DIRECTION_WEST) {
			if(dir == WM_DIRECTION_WEST)
				dw = distw;
			else
				dw = -distw;
		} else if(key_resize_edge == WM_DIRECTION_EAST) {
			if(dir == WM_DIRECTION_EAST)
				dw = distw;
			else
				dw = -distw;
		} else if(key_resize_edge == WM_DIRECTION_NORTH) {
			if(dir == WM_DIRECTION_NORTH)
				dh = disth;
			else
				dh = -disth;
		} else {
			if(dir == WM_DIRECTION_SOUTH)
				dh = disth;
			else
				dh = -disth;
		}
	}
	if(moveresize_client->max_horz && (key_resize_edge == WM_DIRECTION_WEST || key_resize_edge == WM_DIRECTION_EAST)) {
		was_max_horz = TRUE;
		pre_max_area.x = moveresize_client->pre_max_area.x;
		pre_max_area.width = moveresize_client->pre_max_area.width;
		moveresize_client->pre_max_area.x = cur_x;
		moveresize_client->pre_max_area.width = cur_w;
		client_maximize(moveresize_client, FALSE, 1);
	} else if(moveresize_client->max_vert && (key_resize_edge == WM_DIRECTION_NORTH
			|| key_resize_edge == WM_DIRECTION_SOUTH)) {
		was_max_vert = TRUE;
		pre_max_area.y = moveresize_client->pre_max_area.y;
		pre_max_area.height = moveresize_client->pre_max_area.height;
		moveresize_client->pre_max_area.y = cur_y;
		moveresize_client->pre_max_area.height = cur_h;
		client_maximize(moveresize_client, FALSE, 2);
	}
	calc_resize(TRUE, resist, &dw, &dh, dir);
	if(key_resize_edge == WM_DIRECTION_WEST)
		cur_x -= dw;
	else if(key_resize_edge == WM_DIRECTION_NORTH)
		cur_y -= dh;
	cur_w += dw;
	cur_h += dh;
	if(key_resize_edge == WM_DIRECTION_WEST)
		pdx = -dw;
	else if(key_resize_edge == WM_DIRECTION_EAST)
		pdx = dw;
	else if(key_resize_edge == WM_DIRECTION_NORTH)
		pdy = -dh;
	else if(key_resize_edge == WM_DIRECTION_SOUTH)
		pdy = dh;
	screen_pointer_pos(&opx, &opy);
	XWarpPointer(t_display, None, None, 0, 0, 0, 0, pdx, pdy);
	XSync(t_display, FALSE); {
		XEvent ce;
		while(xqueue_remove_local(&ce, xqueue_match_type, GINT_TO_POINTER(MotionNotify)));
	}
	screen_pointer_pos(&px, &py);
	do_resize();
	start_x += (px - opx) - dw;
	start_y += (py - opy) - dh;
}

gboolean moveresize_event(XEvent *e)
{
	gboolean used = FALSE;
	if(!moveresize_in_progress)
		return FALSE;
	if(e->type == ButtonPress) {
		if(!button) {
			start_x = e->xbutton.x_root;
			start_y = e->xbutton.y_root;
			button = e->xbutton.button;
		}
		used = e->xbutton.button == button;
	} else if(e->type == ButtonRelease) {
		if(!button || e->xbutton.button == button) {
			moveresize_end(FALSE);
			used = TRUE;
		}
	} else if(e->type == MotionNotify) {
		if(moving) {
			cur_x = start_cx + e->xmotion.x_root - start_x;
			cur_y = start_cy + e->xmotion.y_root - start_y;
			do_move(FALSE, 0);
			do_edge_warp(e->xmotion.x_root, e->xmotion.y_root);
		} else {
			gint dw, dh;
			enum wm_direction dir;
			if(cur_type == WM_SIZE_TOPLEFT) {
				dw = -(e->xmotion.x_root - start_x);
				dh = -(e->xmotion.y_root - start_y);
				dir = WM_DIRECTION_NORTHWEST;
			} else if(cur_type == WM_SIZE_TOP) {
				dw = 0;
				dh = -(e->xmotion.y_root - start_y);
				dir = WM_DIRECTION_NORTH;
			} else if(cur_type == WM_SIZE_TOPRIGHT) {
				dw = (e->xmotion.x_root - start_x);
				dh = -(e->xmotion.y_root - start_y);
				dir = WM_DIRECTION_NORTHEAST;
			} else if(cur_type == WM_SIZE_RIGHT) {
				dw = (e->xmotion.x_root - start_x);
				dh = 0;
				dir = WM_DIRECTION_EAST;
			} else if(cur_type == WM_SIZE_BOTTOMRIGHT) {
				dw = (e->xmotion.x_root - start_x);
				dh = (e->xmotion.y_root - start_y);
				dir = WM_DIRECTION_SOUTHEAST;
			} else if(cur_type == WM_SIZE_BOTTOM) {
				dw = 0;
				dh = (e->xmotion.y_root - start_y);
				dir = WM_DIRECTION_SOUTH;
			} else if(cur_type == WM_SIZE_BOTTOMLEFT) {
				dw = -(e->xmotion.x_root - start_x);
				dh = (e->xmotion.y_root - start_y);
				dir = WM_DIRECTION_SOUTHWEST;
			} else if(cur_type == WM_SIZE_LEFT) {
				dw = -(e->xmotion.x_root - start_x);
				dh = 0;
				dir = WM_DIRECTION_WEST;
			} else if(cur_type == WM_SIZE_KEYBOARD) {
				dw = (e->xmotion.x_root - start_x);
				dh = (e->xmotion.y_root - start_y);
				dir = WM_DIRECTION_SOUTHEAST;
			} else
				g_assert_not_reached();
			if(ABS(dw) >= config_resist_edge) {
				if(moveresize_client->max_horz) {
					was_max_horz = TRUE;
					pre_max_area.x = moveresize_client->pre_max_area.x;
					pre_max_area.width = moveresize_client->pre_max_area.width;
					moveresize_client->pre_max_area.x = cur_x;
					moveresize_client->pre_max_area.width = cur_w;
					client_maximize(moveresize_client, FALSE, 1);
				}
			} else if(was_max_horz && !moveresize_client->max_horz) {
				client_maximize(moveresize_client, TRUE, 1);
				moveresize_client->pre_max_area.x = pre_max_area.x;
				moveresize_client->pre_max_area.width = pre_max_area.width;
			}
			if(ABS(dh) >= config_resist_edge) {
				if(moveresize_client->max_vert) {
					was_max_vert = TRUE;
					pre_max_area.y = moveresize_client->pre_max_area.y;
					pre_max_area.height = moveresize_client->pre_max_area.height;
					moveresize_client->pre_max_area.y = cur_y;
					moveresize_client->pre_max_area.height = cur_h;
					client_maximize(moveresize_client, FALSE, 2);
				}
			} else if(was_max_vert && !moveresize_client->max_vert) {
				client_maximize(moveresize_client, TRUE, 2);
				moveresize_client->pre_max_area.y = pre_max_area.y;
				moveresize_client->pre_max_area.height = pre_max_area.height;
			}
			dw -= cur_w - start_cw;
			dh -= cur_h - start_ch;
			calc_resize(FALSE, 0, &dw, &dh, dir);
			cur_w += dw;
			cur_h += dh;
			if(cur_type == WM_SIZE_TOPLEFT || cur_type == WM_SIZE_LEFT || cur_type == WM_SIZE_BOTTOMLEFT) {
				cur_x -= dw;
			}
			if(cur_type == WM_SIZE_TOPLEFT || cur_type == WM_SIZE_TOP || cur_type == WM_SIZE_TOPRIGHT) {
				cur_y -= dh;
			}
			do_resize();
		}
		used = TRUE;
	} else if(e->type == KeyPress) {
		KeySym sym = t_keyboard_keypress_to_keysym(e);
		if(sym == XK_Escape) {
			moveresize_end(TRUE);
			used = TRUE;
		} else if(sym == XK_Return || sym == XK_KP_Enter) {
			moveresize_end(FALSE);
			used = TRUE;
		} else if(sym == XK_Right || sym == XK_Left || sym == XK_Up || sym == XK_Down) {
			if(cur_type == WM_SIZE_KEYBOARD) {
				resize_with_keys(sym, e->xkey.state);
				used = TRUE;
			} else if(cur_type == WM_MOVE_KEYBOARD) {
				move_with_keys(sym, e->xkey.state);
				used = TRUE;
			}
		}
	} else if(e->type == t_display_extension_sync_basep + XSyncAlarmNotify) {
		waiting_for_sync = 0;
		do_resize();
		used = TRUE;
	}
	if(used && moveresize_client == focus_client)
		event_update_user_time();
	return used;
}
